package com.usefullc.system.service;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.usefullc.common.enums.QueryExpressTypeEnum;
import com.usefullc.common.exception.BizException;
import com.usefullc.system.entity.BaseEntity;
import com.usefullc.system.entity.Pager;
import com.usefullc.system.query.BaseQuery;
import com.usefullc.system.service.component.extend.UsefulQueryField;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.*;

public abstract class AbstractBizService<M extends BaseMapper<T>, T extends BaseEntity, Q extends BaseQuery> {

//    public BaseDao<T, Serializable> baseDao;

    protected Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    protected M baseMapper;

    public M getBaseMapper() {
        return baseMapper;
    }

    protected InnerService<M, T> service;

    protected Class<M> currentMapperClass() {
        return (Class<M>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    protected Class<T> currentModelClass() {
        return (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1];
    }


    public AbstractBizService() {
    }

    @PostConstruct
    public void init() {
        service = new InnerService<>();
        service.init(baseMapper, currentModelClass(), currentMapperClass());

    }


    protected QueryWrapper<T> getQueryWrapper(Q query) {
        QueryWrapper<T> wrapper = new QueryWrapper<>();
        try {

            List<Field> queryFiledList = FieldUtils.getAllFieldsList(query.getClass());
            for (Field queryField : queryFiledList) {
                if (StringUtils.equalsIgnoreCase(queryField.getName(), "serialVersionUID")) {
                    continue;
                }
                queryField.setAccessible(true);
                Object fieldValue = queryField.get(query);
                queryField.setAccessible(false);
                if (fieldValue == null || StringUtils.isBlank(fieldValue.toString())) {
                    continue;
                }
                UsefulQueryField usefulQueryField = queryField.getAnnotation(UsefulQueryField.class);
                if (usefulQueryField == null) {  //不自动查询处理
                    continue;
                }
                String format = usefulQueryField.format();
                QueryExpressTypeEnum expressTypeEnum = usefulQueryField.type();
                String columnName = queryField.getName();
                if(StringUtils.isNotEmpty(usefulQueryField.columnName())){
                    columnName = usefulQueryField.columnName();
                }else{
                    Field entityFiled = FieldUtils.getField(this.service.getEntityClass(), queryField.getName(), true);
                    if (entityFiled != null) {
                        TableField tableField = entityFiled.getAnnotation(TableField.class);
                        if (tableField != null && StringUtils.isNotEmpty(tableField.value())) {
                            columnName = tableField.value();
                        }
                    }
                }
//                if(StringUtils.isEmpty(columnName)){
//                    throw new BizException("必须设置好字段名称,queryFiled="+queryField);
//                }
                if(StringUtils.isEmpty(format)){
                    switch (expressTypeEnum) {
                        case EQ:
                            wrapper.eq(columnName, fieldValue);
                            break;
                        case GT:
                            wrapper.gt(columnName, fieldValue);
                            break;
                        case LT:
                            wrapper.lt(columnName, fieldValue);
                            break;
                        case GE:
                            wrapper.ge(columnName, fieldValue);
                            break;
                        case LE:
                            wrapper.lt(columnName, fieldValue);
                            break;
                        case LIKE:
                            wrapper.like(columnName, fieldValue);
                            break;
                        case LIKE_LEFT:
                            wrapper.likeLeft(columnName, fieldValue);
                            break;
                        case LIKE_RIGHT:
                            wrapper.likeRight(columnName, fieldValue);
                            break;
                        case IN:
                            if(fieldValue instanceof ArrayList){
                                wrapper.in(columnName,(ArrayList)fieldValue);
                            }else{
                                wrapper.in(columnName,fieldValue);
                            }
                            break;
                    }
                }else {
                    wrapper.apply( "date_format ("+columnName+",'"+format+"') "+expressTypeEnum.getOperator()+" date_format('" + fieldValue.toString() + "','"+format+"')");
                }



            }

            //针对时间
            wrapper.apply(StringUtils.isNotBlank(query.getGmtCreateStart()),
                    "date_format (gmtCreate,'%Y-%m-%d') >= date_format('" + query.getGmtCreateStart() + "','%Y-%m-%d')")
                    .apply(StringUtils.isNotBlank(query.getGmtCreateEnd()),
                            "date_format (gmtCreate,'%Y-%m-%d') <= date_format('" + query.getGmtCreateEnd() + "','%Y-%m-%d')");

            String orderByColumn = query.getOrderByColumn();
            if (StringUtils.isNotEmpty(orderByColumn)) {
                String[] orderByColumns = orderByColumn.split(",");
                if (StringUtils.equalsIgnoreCase(query.getAscOrDesc(), "asc")) {
                    wrapper.orderByAsc(Arrays.asList(orderByColumns));
                } else {
                    wrapper.orderByDesc(Arrays.asList(orderByColumns));
                }
            }
            Integer resultLimit = query.getResultLimit();
            if (resultLimit != null) {
                wrapper.last("limit " + resultLimit);
            }

        } catch (Exception e) {
            throw new BizException(e);
        }
        return wrapper;
    }

    public boolean save(T entity) {
        if (entity.getId() != null) {
            return this.update(entity);
        } else {
            entity.setGmtCreate(new Date());
            entity.setGmtModified(new Date());
            return this.service.save(entity);
        }
    }

    public boolean update(T entity) {
        if (entity.getId() == null) {
            throw new BizException("id不能为空");
        }
        entity.setGmtModified(new Date());
        return this.service.updateById(entity);
    }
//    public boolean saveOrUpdate(T entity) {
//        return this.service.saveOrUpdate(entity);
//    }


    public boolean saveAll(Collection<T> entityList) {
        Date now = new Date();
        entityList.stream().forEach(data -> {
            if (data.getId() == null) {
                data.setGmtCreate(now);

            }
            data.setGmtModified(now);
        });
        return this.service.saveBatch(entityList);
    }

    public void deletePhysByIds(List<Long> ids) {
        List<Serializable> newIds = new ArrayList<>();
        for (Long id : ids) {
            newIds.add(id);
        }
        this.service.removeByIds(newIds);
    }

    public void deletePhysById(Long id) {
        this.service.removeById(id);
    }

    public void delete(List<Long> ids) {
        UpdateWrapper<T> wrapper = new UpdateWrapper<>();
        wrapper.set("delState", false);
        wrapper.set("gmtModified", new Date());
        wrapper.in("id", ids);
        this.service.update(wrapper);
    }

    public void delete(Long id) {
        List<Long> ids = new ArrayList<>();
        ids.add(id);
        this.service.removeByIds(ids);
    }


    public int executeUpdate(String sql) {
//        return this.service.getBaseMapper().(sql);
        return 0; //todo
    }

    public T findById(Serializable id) {
        T entity = this.service.getById(id);
        return entity;
    }

    public T findOne(Q q) {
        QueryWrapper<T> wrapper = getQueryWrapper(q);
        return this.service.getOne(wrapper, false);
//        if(q.getResultLimit() != null){
//            if(q.getResultLimit() != 1){
//                throw new BizException("findOne limit must be 1");
//            }
//            wrapper.last("limit 1");
//            return (T) this.service.getOne(wrapper);
//        }else{
//            return (T) this.service.getOne(wrapper);
//        }
    }

    public List<T> findAll(Q q) {
        QueryWrapper wrapper = getQueryWrapper(q);
        return this.service.list(wrapper);
    }

    public long count(Q q){
        QueryWrapper wrapper = getQueryWrapper(q);
        long total = this.service.count(wrapper);
        return total;
    }

    public Pager<T> findByPage(Q q) {
        if (q.getPageIndex() <= 0) {
            q.setPageIndex(1);
        }
        Pager<T> pager = new Pager<>(q.getPageIndex(), q.getPageSize());

        long total = this.count(q);
        if (total == 0) {
            return pager;
        } else {
            pager.setTotal(total);
        }
        Page<T> page = new Page<>(q.getPageIndex(), q.getPageSize());
        QueryWrapper wrapper = getQueryWrapper(q);
        Page newPage = this.service.page(page, wrapper);

        pager.setDataList(newPage.getRecords());
        return pager;

    }


}
